/***
 * Excerpted from "OpenGL ES for Android",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/kbogla for more book information.
***/
package net.kesumu.general.android.opengl.object;

import static android.opengl.GLES20.GL_TRIANGLE_FAN;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.glDrawArrays;

import java.security.KeyStore.Builder;
import java.util.ArrayList;
import java.util.List;

import net.kesumu.general.android.opengl.object.Geometry.Circle;
import net.kesumu.general.android.opengl.object.Geometry.Cylinder;
import net.kesumu.general.android.opengl.object.Geometry.Line;
import net.kesumu.general.android.opengl.object.Geometry.Point;
import net.kesumu.general.android.opengl.object.Geometry.RingSector;
import net.kesumu.general.android.opengl.object.Geometry.Sector;
import android.graphics.Color;
import android.util.FloatMath;


class ObjectBuilder {
    private static final int FLOATS_PER_VERTEX = 3;
    private static final int FLOATS_PER_VERTEX_AND_COLOR = 6;
    private static final int FLOATS_PER_VERTEX_AND_TEXTURE = 5;

    static interface DrawCommand {
        void draw();
    }

    static class GeneratedData {
        final float[] vertexData;
        final List<DrawCommand> drawList;

        GeneratedData(float[] vertexData, List<DrawCommand> drawList) {
            this.vertexData = vertexData;
            this.drawList = drawList;
        }
    }
    
    static GeneratedData createYushabuMessageItem(RingSector bgRingSector
                                                 ,RingSector fgRingSector
                                                 ,Circle bgCircle
                                                 ,Circle fgCircle
                                                 ,int color
                                                 ,int numPoints) {
        int size = sizeOfRingSectorInVertices(numPoints)*2
                 + sizeOfCircleInVertices(numPoints)*2;
        
        int vertexWithColorVarying = 0;
        ObjectBuilder builder = new ObjectBuilder(size,vertexWithColorVarying);
        
        int toColor = Color.rgb((Color.red(color) + 255)/2
                                , (Color.green(color) + 255)/2
                                , (Color.blue(color) + 255)/2);
        
        builder.appendRingSector(bgRingSector, Color.WHITE, Color.WHITE, 0f, numPoints);
        builder.appendRingSector(fgRingSector, color, toColor, 0f, numPoints);
        builder.appendCircleXY(bgCircle, Color.WHITE, Color.WHITE, numPoints);
        builder.appendCircleXY(fgCircle, color, toColor, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createYushabuMessageContent(RingSector fgRingSector
                                                 ,int numPoints) {
        int size = sizeOfRingSectorInVertices(numPoints);
        
        float textured = 0;
        ObjectBuilder builder = new ObjectBuilder(size,textured);
        
        builder.appendTexturedRingSector(fgRingSector, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createYushabuHistoryLine(RingSector bgRingSector
                                                 ,RingSector fgRingSector
                                                 ,int color
                                                 ,int numPoints) {
        int size = sizeOfRingSectorInVertices(numPoints)*2;
       
        int vertexWithColorVarying = 0;
        ObjectBuilder builder = new ObjectBuilder(size,vertexWithColorVarying);
       
        int toColor = Color.rgb((Color.red(color) + 255)/2
                , (Color.green(color) + 255)/2
                , (Color.blue(color) + 255)/2);
        
        builder.appendRingSector(bgRingSector, Color.WHITE, Color.WHITE, 0f, numPoints);
        builder.appendRingSector(fgRingSector, color, toColor, 0.5f, numPoints);
       
        return builder.build();
    }
    
    static GeneratedData createYushabuHistoryBackground(Sector sector, int color, int numPoints) {
        int size = sizeOfSectorInVertices(numPoints)*2;
        
        int vertexWithColorVarying = 0;
        ObjectBuilder builder = new ObjectBuilder(size,vertexWithColorVarying);
       
        builder.appendSectorXY(sector, color, color, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createYushabuPhotoBackground(Circle bgCircle1, Circle bgCircle2, Circle fgCircle
                                            ,int color, int numPoints) {
        int size = sizeOfCircleInVertices(numPoints)*3;
        
        ObjectBuilder builder = new ObjectBuilder(size,color);
       
        builder.appendCircleXY(bgCircle1, Color.WHITE, Color.WHITE, numPoints);
        builder.appendCircleXY(bgCircle2, Color.WHITE, Color.WHITE, numPoints);
        builder.appendCircleXY(fgCircle, color, color, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createFocusedYushabuPhotoBackground(Circle focusedBgCircle, Circle focusedFgCircle
                                            ,int color, int numPoints) {
        int size = sizeOfCircleInVertices(numPoints)*2;
        
        ObjectBuilder builder = new ObjectBuilder(size,color);
        
        builder.appendCircleXY(focusedBgCircle, Color.WHITE, Color.WHITE, numPoints);
        builder.appendCircleXY(focusedFgCircle, color, color, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createYushabuPhoto(Circle circle, int numPoints) {
        int size = sizeOfCircleInVertices(numPoints);
        
        float textured = 0f;
        
        ObjectBuilder builder = new ObjectBuilder(size,textured);
        
        builder.appendTexturedCircleXY(circle, numPoints);
        
        return builder.build();
    }
    
    static GeneratedData createYushabuMessageLine(Line line, float width, int color, int numPoints) {
        int size = sizeOfLineInVertices(numPoints);
        
        ObjectBuilder builder = new ObjectBuilder(size,color);
        
        builder.appendLine(line, width, color, color, 0f, numPoints);
        
        return builder.build();
    }

    static GeneratedData createPuck(Cylinder puck, int numPoints) {
        int size = sizeOfCircleInVertices(numPoints)
                 + sizeOfOpenCylinderInVertices(numPoints);
        
        ObjectBuilder builder = new ObjectBuilder(size);

        Circle puckTop = new Circle(
            puck.center.translateY(puck.height / 2f),
            puck.radius);
        
        builder.appendCircle(puckTop, numPoints);
        builder.appendOpenCylinder(puck, numPoints);

        return builder.build();
    }
    
    static GeneratedData createMallet(
        Point center, float radius, float height, int numPoints) {
        int size = sizeOfCircleInVertices(numPoints) * 2
                 + sizeOfOpenCylinderInVertices(numPoints) * 2;
        
        ObjectBuilder builder = new ObjectBuilder(size);                                      
        
        // First, generate the mallet base.
        float baseHeight = height * 0.25f;
        
        Circle baseCircle = new Circle(
            center.translateY(-baseHeight), 
            radius);
        Cylinder baseCylinder = new Cylinder(
            baseCircle.center.translateY(-baseHeight / 2f), 
            radius, baseHeight);

        builder.appendCircle(baseCircle, numPoints);
        builder.appendOpenCylinder(baseCylinder, numPoints);
                
        // Now generate the mallet handle.
        float handleHeight = height * 0.75f;
        float handleRadius = radius / 3f;
        
        Circle handleCircle = new Circle(
            center.translateY(height * 0.5f), 
            handleRadius);        
        Cylinder handleCylinder = new Cylinder(
            handleCircle.center.translateY(-handleHeight / 2f),
            handleRadius, handleHeight);                

        builder.appendCircle(handleCircle, numPoints);
        builder.appendOpenCylinder(handleCylinder, numPoints);

        return builder.build();
    }    

    private static int sizeOfCircleInVertices(int numPoints) {
        return 1 + (numPoints + 1);
    }

    private static int sizeOfOpenCylinderInVertices(int numPoints) {
        return (numPoints + 1) * 2;
    }
    
    private static int sizeOfRingSectorInVertices(int numPoints) {
        return (numPoints + 1) * 2;
    }
    
    private static int sizeOfSectorInVertices(int numPoints) {
        return 1 + (numPoints + 1);
    }
    
    private static int sizeOfLineInVertices(int numPoints) {
        return (numPoints + 1) * 2;
    }

    private final float[] vertexData;
    private final List<DrawCommand> drawList = new ArrayList<DrawCommand>();
    private int offset = 0;

    private ObjectBuilder(int sizeInVertices) {
        vertexData = new float[sizeInVertices * FLOATS_PER_VERTEX];
    }
    private ObjectBuilder(int sizeInVertices, int color) {
        vertexData = new float[sizeInVertices * FLOATS_PER_VERTEX_AND_COLOR];
    }
    private ObjectBuilder(int sizeInVertices, float textured) {
        vertexData = new float[sizeInVertices * FLOATS_PER_VERTEX_AND_TEXTURE];
    }
    
    private void appendLine(Line line, float width, int startColor, int endColor, float lightSpot, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_COLOR;
        final int numVertices = sizeOfLineInVertices(numPoints);
        
        float R = (float)Color.red(startColor) / 255f;
        float G = (float)Color.green(startColor) / 255f;
        float B = (float)Color.blue(startColor) / 255f;
        
        float toR = (float)Color.red(endColor) / 255f;
        float toG = (float)Color.green(endColor) / 255f;
        float toB = (float)Color.blue(endColor) / 255f;
        
        float deltX = line.endPoint.x - line.startPoint.x;
        float deltY = line.endPoint.y - line.startPoint.y;
        float deltZ = line.endPoint.z - line.startPoint.z;
        // Generate strip around center point. <= is used because we want to
        // generate the points at the starting angle twice, to complete the
        // strip.
        for (int i = 0; i <= numPoints; i++) {
            float factor = ((float) i / (float) numPoints);
            
            float xPositionInSide = line.startPoint.x + deltX * factor;
            
            float yPositionInside = line.startPoint.y + deltY * factor;
            
            float zPositionInside = line.startPoint.z + deltZ * factor;
            
            float xPositionOutSide = xPositionInSide - 0.5f*width;
              
            float yPositionOutside = yPositionInside - 0.5f*width;
            
            float zPositionOutside = zPositionInside;

            vertexData[offset++] = xPositionInSide;
            vertexData[offset++] = yPositionInside;
            vertexData[offset++] = zPositionInside;

            float colorVaryingFactor;
            if( lightSpot < 0.01f) {
                colorVaryingFactor = factor;
            }else if(factor < lightSpot) {
                colorVaryingFactor = (lightSpot - factor)/lightSpot;
            }else {
                colorVaryingFactor = (factor - lightSpot)/lightSpot;
            }
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toR)  
                                 + (colorVaryingFactor)*R;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toG)  
                                 + (colorVaryingFactor)*G;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toB)  
                                 + (colorVaryingFactor)*B;

            vertexData[offset++] = xPositionOutSide;
            vertexData[offset++] = yPositionOutside;
            vertexData[offset++] = zPositionOutside;
            
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toR) 
                                 + (colorVaryingFactor)*R;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toG)  
                                 + (colorVaryingFactor)*G;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toB) 
                                 + (colorVaryingFactor)*B;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertices);
            }
        });      
    }
    
    private void appendSectorXY(Sector sector, int startColor, int endColor, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_COLOR;
        final int numVertices = sizeOfSectorInVertices(numPoints);
        
        float R = (float)Color.red(startColor) / 255f;
        float G = (float)Color.green(startColor) / 255f;
        float B = (float)Color.blue(startColor) / 255f;
        
        float toR = (float)Color.red(endColor) / 255f;
        float toG = (float)Color.green(endColor) / 255f;
        float toB = (float)Color.blue(endColor) / 255f;

        // Center point of fan
        vertexData[offset++] = sector.center.x;
        vertexData[offset++] = sector.center.y;
        vertexData[offset++] = sector.center.z;
        
        vertexData[offset++] = 0.5f*(toR) + 0.5f*R;
        vertexData[offset++] = 0.5f*(toG) + 0.5f*G;
        vertexData[offset++] = 0.5f*(toB) + 0.5f*B;

        float startAngleInRadians = 
                (sector.startAngle / 360f) * ((float) Math.PI * 2f);
        float endAngleInRadians = 
                (sector.endAngle / 360f) * ((float) Math.PI * 2f);
        // Fan around center point. <= is used because we want to generate
        // the point at the starting angle twice to complete the fan.
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = startAngleInRadians + 
                    ((float) i / (float) numPoints)
                  * (endAngleInRadians - startAngleInRadians);
            
            vertexData[offset++] = 
                sector.center.x 
                + sector.radius * FloatMath.cos(angleInRadians);
            vertexData[offset++] = sector.center.y + 
                + sector.radius * FloatMath.sin(angleInRadians); ;
            vertexData[offset++] = sector.center.z;  
            
            //add the varying color
            float colorVaryingFactor = ((float) i / (float) (numPoints/2f));
            vertexData[offset++] = colorVaryingFactor*(toR)  
                                 + (1.0f - colorVaryingFactor)*R;
            vertexData[offset++] = colorVaryingFactor*(toG)  
                                 + (1.0f - colorVaryingFactor)*G;
            vertexData[offset++] = colorVaryingFactor*(toB)  
                                 + (1.0f - colorVaryingFactor)*B;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startVertex, numVertices);
            }
        });
    }
    
    private void appendCircleXY(Circle circle, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertices = sizeOfCircleInVertices(numPoints);

        // Center point of fan
        vertexData[offset++] = circle.center.x;
        vertexData[offset++] = circle.center.y;
        vertexData[offset++] = circle.center.z;

        // Fan around center point. <= is used because we want to generate
        // the point at the starting angle twice to complete the fan.
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = 
                  ((float) i / (float) numPoints)
                * ((float) Math.PI * 2f);
            
            vertexData[offset++] = 
                  circle.center.x 
                + circle.radius * FloatMath.cos(angleInRadians);
            vertexData[offset++] = circle.center.y + 
                + circle.radius * FloatMath.sin(angleInRadians); ;
            vertexData[offset++] = circle.center.z;            
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startVertex, numVertices);
            }
        });
    }
    
    private void appendTexturedCircleXY(Circle circle, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_TEXTURE;
        final int numVertices = sizeOfCircleInVertices(numPoints);

        // Center point of fan
        vertexData[offset++] = circle.center.x;
        vertexData[offset++] = circle.center.y;
        vertexData[offset++] = circle.center.z;
        
        vertexData[offset++] = 0.5f;
        vertexData[offset++] = 0.5f;

        // Fan around center point. <= is used because we want to generate
        // the point at the starting angle twice to complete the fan.
        for (int i = 0; i <= numPoints; i++) {
           float angleInRadians = ((float) i / (float) numPoints) * ((float) Math.PI * 2f);
              
           vertexData[offset++] = circle.center.x + circle.radius * FloatMath.cos(angleInRadians);
           vertexData[offset++] = circle.center.y + circle.radius * FloatMath.sin(angleInRadians);
           vertexData[offset++] = circle.center.z;   
            
           float textureAngleRadians = (float) Math.PI / 2f + angleInRadians;
           
           vertexData[offset++] = FloatMath.sin(textureAngleRadians)/2.0f + 0.5f;
           vertexData[offset++] = FloatMath.cos(textureAngleRadians)/2.0f + 0.5f;
        }
        
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startVertex, numVertices);
            }
        });
    }
    
    private void appendCircleXY(Circle circle, int startColor, int endColor, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_COLOR;
        final int numVertices = sizeOfCircleInVertices(numPoints);
        
        float R = (float)Color.red(startColor) / 255f;
        float G = (float)Color.green(startColor) / 255f;
        float B = (float)Color.blue(startColor) / 255f;
        
        float toR = (float)Color.red(endColor) / 255f;
        float toG = (float)Color.green(endColor) / 255f;
        float toB = (float)Color.blue(endColor) / 255f;

        // Center point of fan
        vertexData[offset++] = circle.center.x;
        vertexData[offset++] = circle.center.y;
        vertexData[offset++] = circle.center.z;
        
        vertexData[offset++] = 0.5f*(toR) + 0.5f*R;
        vertexData[offset++] = 0.5f*(toG) + 0.5f*G;
        vertexData[offset++] = 0.5f*(toB) + 0.5f*B;

        // Fan around center point. <= is used because we want to generate
        // the point at the starting angle twice to complete the fan.
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = 
                  ((float) i / (float) numPoints)
                * ((float) Math.PI * 2f);
            
            vertexData[offset++] = circle.center.x + circle.radius * FloatMath.cos(angleInRadians);
            vertexData[offset++] = circle.center.y + circle.radius * FloatMath.sin(angleInRadians);
            vertexData[offset++] = circle.center.z;  
            
            //add the varying color
            if(i < numPoints/2) {
                float colorVaryingFactor = ((float) i / (float) (numPoints/2f));
                vertexData[offset++] = colorVaryingFactor*(toR)  
                                     + (1.0f - colorVaryingFactor)*R;
                vertexData[offset++] = colorVaryingFactor*(toG)  
                                     + (1.0f - colorVaryingFactor)*G;
                vertexData[offset++] = colorVaryingFactor*(toB)  
                                     + (1.0f - colorVaryingFactor)*B;
            }else {
                float colorVaryingFactor = ((float) (numPoints-i) / (float) (numPoints/2f));
                vertexData[offset++] = colorVaryingFactor*(toR)  
                        + (1.0f - colorVaryingFactor)*R;
                vertexData[offset++] = colorVaryingFactor*(toG)  
                                     + (1.0f - colorVaryingFactor)*G;
                vertexData[offset++] = colorVaryingFactor*(toB)  
                                     + (1.0f - colorVaryingFactor)*B;
            }
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startVertex, numVertices);
            }
        });
    }

    private void appendCircle(Circle circle, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertices = sizeOfCircleInVertices(numPoints);

        // Center point of fan
        vertexData[offset++] = circle.center.x;
        vertexData[offset++] = circle.center.y;
        vertexData[offset++] = circle.center.z;

        // Fan around center point. <= is used because we want to generate
        // the point at the starting angle twice to complete the fan.
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = 
                  ((float) i / (float) numPoints)
                * ((float) Math.PI * 2f);
            
            vertexData[offset++] = 
                  circle.center.x 
                + circle.radius * FloatMath.cos(angleInRadians);
            vertexData[offset++] = circle.center.y;
            vertexData[offset++] = 
                  circle.center.z 
                + circle.radius * FloatMath.sin(angleInRadians);            
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startVertex, numVertices);
            }
        });
    }
    
    private void appendRingSector(RingSector ringSector, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertices = sizeOfRingSectorInVertices(numPoints);

        // Generate strip around center point. <= is used because we want to
        // generate the points at the starting angle twice, to complete the
        // strip.
        float startAngleInRadians = 
                (ringSector.startAngle / 360f) * ((float) Math.PI * 2f);
        float endAngleInRadians = 
                (ringSector.endAngle / 360f) * ((float) Math.PI * 2f);
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = startAngleInRadians + 
                  ((float) i / (float) numPoints)
                * (endAngleInRadians - startAngleInRadians);
            
            float xPositionInSide = 
                    ringSector.center.x 
                + ringSector.radiusInside * FloatMath.cos(angleInRadians);
            
            float yPositionInside = 
                    ringSector.center.y 
                + ringSector.radiusInside * FloatMath.sin(angleInRadians);
            
            float xPositionOutSide = 
                    ringSector.center.x 
                  + ringSector.radiusOutside * FloatMath.cos(angleInRadians);
              
            float yPositionOutside = 
                    ringSector.center.y 
                  + ringSector.radiusOutside * FloatMath.sin(angleInRadians);

            vertexData[offset++] = xPositionInSide;
            vertexData[offset++] = yPositionInside;
            vertexData[offset++] = ringSector.center.z;

            vertexData[offset++] = xPositionOutSide;
            vertexData[offset++] = yPositionOutside;
            vertexData[offset++] = ringSector.center.z;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertices);
            }
        });        
    }
    
    private void appendRingSector(RingSector ringSector, int startColor, int endColor, float lightSpot, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_COLOR;
        final int numVertices = sizeOfRingSectorInVertices(numPoints);
        
        float R = (float)Color.red(startColor) / 255f;
        float G = (float)Color.green(startColor) / 255f;
        float B = (float)Color.blue(startColor) / 255f;
        
        float toR = (float)Color.red(endColor) / 255f;
        float toG = (float)Color.green(endColor) / 255f;
        float toB = (float)Color.blue(endColor) / 255f;

        // Generate strip around center point. <= is used because we want to
        // generate the points at the starting angle twice, to complete the
        // strip.
        float startAngleInRadians = 
                (ringSector.startAngle / 360f) * ((float) Math.PI * 2f);
        float endAngleInRadians = 
                (ringSector.endAngle / 360f) * ((float) Math.PI * 2f);
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = startAngleInRadians + 
                  ((float) i / (float) numPoints)
                * (endAngleInRadians - startAngleInRadians);
            
            float xPositionInSide = 
                    ringSector.center.x 
                + ringSector.radiusInside * FloatMath.cos(angleInRadians);
            
            float yPositionInside = 
                    ringSector.center.y 
                + ringSector.radiusInside * FloatMath.sin(angleInRadians);
            
            float xPositionOutSide = 
                    ringSector.center.x 
                  + ringSector.radiusOutside * FloatMath.cos(angleInRadians);
              
            float yPositionOutside = 
                    ringSector.center.y 
                  + ringSector.radiusOutside * FloatMath.sin(angleInRadians);

            vertexData[offset++] = xPositionInSide;
            vertexData[offset++] = yPositionInside;
            vertexData[offset++] = ringSector.center.z;
            
            float factor = ((float) i / (float) numPoints);
            float colorVaryingFactor;
            if( lightSpot < 0.01f) {
                colorVaryingFactor = factor;
            }else if(factor < lightSpot) {
                colorVaryingFactor = (lightSpot - factor)/lightSpot;
            }else {
                colorVaryingFactor = (factor - lightSpot)/lightSpot;
            }
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toR)  
                                 + (colorVaryingFactor)*R;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toG)  
                                 + (colorVaryingFactor)*G;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toB)  
                                 + (colorVaryingFactor)*B;

            vertexData[offset++] = xPositionOutSide;
            vertexData[offset++] = yPositionOutside;
            vertexData[offset++] = ringSector.center.z;
            
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toR) 
                                 + (colorVaryingFactor)*R;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toG)  
                                 + (colorVaryingFactor)*G;
            vertexData[offset++] = (1.0f-colorVaryingFactor)*(toB) 
                                 + (colorVaryingFactor)*B;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertices);
            }
        });        
    }
    
    private void appendTexturedRingSector(RingSector ringSector, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX_AND_TEXTURE;
        final int numVertices = sizeOfRingSectorInVertices(numPoints);

        // Generate strip around center point. <= is used because we want to
        // generate the points at the starting angle twice, to complete the
        // strip.
        float startAngleInRadians = 
                (ringSector.startAngle / 360f) * ((float) Math.PI * 2f);
        float endAngleInRadians = 
                (ringSector.endAngle / 360f) * ((float) Math.PI * 2f);
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = startAngleInRadians + 
                  ((float) i / (float) numPoints)
                * (endAngleInRadians - startAngleInRadians);
            
            float xPositionInSide = 
                    ringSector.center.x 
                + ringSector.radiusInside * FloatMath.cos(angleInRadians);
            
            float yPositionInside = 
                    ringSector.center.y 
                + ringSector.radiusInside * FloatMath.sin(angleInRadians);
            
            float xPositionOutSide = 
                    ringSector.center.x 
                  + ringSector.radiusOutside * FloatMath.cos(angleInRadians);
              
            float yPositionOutside = 
                    ringSector.center.y 
                  + ringSector.radiusOutside * FloatMath.sin(angleInRadians);

            vertexData[offset++] = xPositionInSide;
            vertexData[offset++] = yPositionInside;
            vertexData[offset++] = ringSector.center.z;
            
            float factor = ((float) i / (float) numPoints);
            vertexData[offset++] = factor;
            vertexData[offset++] = 0f;

            vertexData[offset++] = xPositionOutSide;
            vertexData[offset++] = yPositionOutside;
            vertexData[offset++] = ringSector.center.z;
            
            vertexData[offset++] = factor;
            vertexData[offset++] = 1f;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertices);
            }
        });        
    }

    private void appendOpenCylinder(Cylinder cylinder, int numPoints) {
        final int startVertex = offset / FLOATS_PER_VERTEX;
        final int numVertices = sizeOfOpenCylinderInVertices(numPoints);
        final float yStart = cylinder.center.y - (cylinder.height / 2f);
        final float yEnd = cylinder.center.y + (cylinder.height / 2f);

        // Generate strip around center point. <= is used because we want to
        // generate the points at the starting angle twice, to complete the
        // strip.
        for (int i = 0; i <= numPoints; i++) {
            float angleInRadians = 
                  ((float) i / (float) numPoints)
                * ((float) Math.PI * 2f);
            
            float xPosition = 
                  cylinder.center.x 
                + cylinder.radius * FloatMath.cos(angleInRadians);
            
            float zPosition = 
                  cylinder.center.z 
                + cylinder.radius * FloatMath.sin(angleInRadians);

            vertexData[offset++] = xPosition;
            vertexData[offset++] = yStart;
            vertexData[offset++] = zPosition;

            vertexData[offset++] = xPosition;
            vertexData[offset++] = yEnd;
            vertexData[offset++] = zPosition;
        }
        drawList.add(new DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startVertex, numVertices);
            }
        });        
    }
    
    

    private GeneratedData build() {
        return new GeneratedData(vertexData, drawList);
    }
}
